<!DOCTYPE html>
<html lang="en"><head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1"><!-- Begin Jekyll SEO tag v2.8.0 -->
<title>0079: Notebook III – Customized Tabs, Part I | gtkDcoding</title>
<meta name="generator" content="Jekyll v4.3.4" />
<meta property="og:title" content="0079: Notebook III – Customized Tabs, Part I" />
<meta name="author" content="Ron Tarrant" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="This GTK Tutorial covers customizing how tabs look." />
<meta property="og:description" content="This GTK Tutorial covers customizing how tabs look." />
<link rel="canonical" href="http://localhost:4000/posts/0079-notebook-iii-custom-tabs-i.html" />
<meta property="og:url" content="http://localhost:4000/posts/0079-notebook-iii-custom-tabs-i.html" />
<meta property="og:site_name" content="gtkDcoding" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2019-10-15T00:00:00-04:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="0079: Notebook III – Customized Tabs, Part I" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"Ron Tarrant"},"dateModified":"2019-10-15T00:00:00-04:00","datePublished":"2019-10-15T00:00:00-04:00","description":"This GTK Tutorial covers customizing how tabs look.","headline":"0079: Notebook III – Customized Tabs, Part I","mainEntityOfPage":{"@type":"WebPage","@id":"http://localhost:4000/posts/0079-notebook-iii-custom-tabs-i.html"},"url":"http://localhost:4000/posts/0079-notebook-iii-custom-tabs-i.html"}</script>
<!-- End Jekyll SEO tag -->
<link rel="stylesheet" href="../assets/main.css">
  <link rel="stylesheet" href="../css/style.css" /><link type="application/atom+xml" rel="alternate" href="http://localhost:4000/feed.xml" title="gtkDcoding" /></head>
<body><header class="site-header" role="banner">

  <div class="wrapper"><a class="site-title" rel="author" href="../">gtkDcoding</a><nav class="site-nav">
        <input type="checkbox" id="nav-trigger" class="nav-trigger" />
        <label for="nav-trigger">
          <span class="menu-icon">
            <svg viewBox="0 0 18 15" width="18px" height="15px">
              <path d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.032C17.335,0,18,0.665,18,1.484L18,1.484z M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.032C17.335,6.031,18,6.696,18,7.516L18,7.516z M18,13.516C18,14.335,17.335,15,16.516,15H1.484 C0.665,15,0,14.335,0,13.516l0,0c0-0.82,0.665-1.483,1.484-1.483h15.032C17.335,12.031,18,12.695,18,13.516L18,13.516z"/>
            </svg>
          </span>
        </label>

        <div class="trigger"><a class="page-link" href="../about.html">About</a><a class="page-link" href="../index-reverse.html">Posts in Date Order</a><a class="page-link" href="../topics.html">Blog Posts by Topic</a></div>
      </nav></div>
</header>
<main class="page-content" aria-label="Content">
      <div class="wrapper">
        <article class="post h-entry" itemscope itemtype="http://schema.org/BlogPosting">
	<header class="post-header">
		<link rel="stylesheet" href="../css/font_size.css" />
		<link rel="stylesheet" href="../css/figure.css" />
		<link rel="stylesheet" href="../css/topics/container.css" />  <!-- sub in the tag/topic -->
		<link rel="stylesheet" href="../css/modal.css" />
		<link rel="stylesheet" href="../css/post.css" />
		<link rel="stylesheet" href="../css/mascot_effects.css" />

		
		
		<p class="post-meta">
		<time class="dt-published" datetime="2019-10-15T00:00:00-04:00" itemprop="datePublished">Tuesday, October 15, 2019
		</time>• by <span itemprop="author" itemscope itemtype="http://schema.org/Person"><span class="p-author h-card" itemprop="name">Ron Tarrant</span></span></p>
	</header>

	<div class="post-content e-content" itemprop="articleBody">
		<div class="skew-line"></div>
		<h1 id="0079-notebook-iii--customized-tabs-part-i">0079: Notebook III – Customized Tabs, Part I</h1>

<p>Here’s a screen-shot of what we’re up to:</p>

<!-- 0, 1 -->
<!-- first occurrence of application and terminal screen shots on a single page -->
<div class="screenshot-frame">
	<div class="frame-header">
		Results of this example:
	</div>
	<div class="frame-screenshot">
		<figure>
			<img id="img0" src="../images/screenshots/014_container/container_16.png" alt="Current example output" />		<!-- img# -->
			
			<!-- Modal for screenshot -->
			<div id="modal0" class="modal">																	<!-- modal# -->
				<span class="close0">&times;</span>															<!-- close# -->
				<img class="modal-content" id="img00" />															<!-- img## -->
				<div id="caption"></div>
			</div>
			
			<script>
			// Get the modal
			var modal = document.getElementById("modal0");														// modal#
			
			// Get the image and insert it inside the modal - use its "alt" text as a caption
			var img = document.getElementById("img0");															// img#
			var modalImg = document.getElementById("img00");													// img##
			var captionText = document.getElementById("caption");

			img.onclick = function()
			{
			  modal.style.display = "block";
			  modalImg.src = this.src;
			  captionText.innerHTML = this.alt;
			}
			
			// Get the <span> element that closes the modal
			var span = document.getElementsByClassName("close0")[0];											// close#
			
			// When the user clicks on <span> (x), close the modal
			span.onclick = function()
			{ 
				modal.style.display = "none";
			}
			</script>
			<figcaption>
			Current example output
			</figcaption>
		</figure>
	</div>

	<div class="frame-terminal">
		<figure class="right">
			<img id="img1" src="../images/screenshots/014_container/container_16_term.png" alt="Current example terminal output" />		<!-- img#, filename -->

			<!-- Modal for terminal shot -->
			<div id="modal1" class="modal">																				<!-- modal# -->
				<span class="close1">&times;</span>																		<!-- close# -->
				<img class="modal-content" id="img11" />																		<!-- img## -->
				<div id="caption"></div>
			</div>
			
			<script>
			// Get the modal
			var modal = document.getElementById("modal1");																	// modal#
			
			// Get the image and insert it inside the modal - use its "alt" text as a caption
			var img = document.getElementById("img1");																		// img#
			var modalImg = document.getElementById("img11");																// img##
			var captionText = document.getElementById("caption");

			img.onclick = function()
			{
			  modal.style.display = "block";
			  modalImg.src = this.src;
			  captionText.innerHTML = this.alt;
			}
			
			// Get the <span> element that closes the modal
			var span = document.getElementsByClassName("close1")[0];														// close#
			
			// When the user clicks on <span> (x), close the modal
			span.onclick = function()
			{ 
				modal.style.display = "none";
			}
			</script>

			<figcaption>
				Current example terminal output (click for enlarged view)
			</figcaption>
		</figure>
	</div>

	<div class="frame-footer">																								<!-- ------------- filename (below) --------- -->
		The code file for this example is available <a href="https://github.com/rontarrant/gtkd_demos/blob/master/014_container/container_16_notebook_multi_custom_tabs.d" target="_blank">here</a>.
	</div>
</div>
<!-- end of snippet for first (1st) occurrence of application and terminal screen shots on a single page -->

<p><code class="language-plaintext highlighter-rouge">Notebook</code> tabs can be pretty much whatever you want. I read in one book that you could even, in theory, stuff an entire other <code class="language-plaintext highlighter-rouge">Notebook</code> inside a <code class="language-plaintext highlighter-rouge">Notebook</code> tab. We’re not going that far, but we will do some cool stuff.</p>

<p>But customized tabs can get tricky if you stuff anything into them besides a <code class="language-plaintext highlighter-rouge">Label</code> or <code class="language-plaintext highlighter-rouge">Image</code> widget. The reason it’s tricky? Because any widget that absorbs mouse clicks will stop the tab from responding to user input.</p>

<div class="inpage-frame">
	<figure class="left">
		<img src="../images/diagrams/014_container/active_tab_area.png" alt="Figure 1: Active tab areas" style="width: 294px; height: 231px;" />
		<figcaption>
			Figure 1: Active tab areas
		</figcaption>
	</figure>
</div>

<p>Okay, well… this isn’t 100% true. A label widget (notice, that’s <em>label</em> with a lowercase ‘<em>l</em>’) will <em>not</em> take up the full space allotted to a tab, so if your user clicks to the immediate left or right of the child widget, the result will still be as expected. But that’s not exactly the kind of thing you wanna be writing up in your docs, so we’re going to cook up a workaround. We’ll let the child widget react and harness that signal to trigger whatever would have been triggered by clicking directly on the tab.</p>

<p><br />
<br /></p>

<h2 id="class-preamble-for-a-notebook-with-customized-tabs">Class Preamble for a Notebook with Customized Tabs</h2>

<p>Our class starts off like this:</p>

<div class="language-d highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PositionType</span> <span class="n">tabPosition</span> <span class="p">=</span> <span class="n">PositionType</span><span class="p">.</span><span class="n">TOP</span><span class="p">;</span>
<span class="nb">string</span> <span class="n">tabLabelPrefix</span> <span class="p">=</span> <span class="s">"Custom Tab"</span><span class="p">;</span>

<span class="n">CustomTab</span> <span class="n">customTab1</span><span class="p">,</span> <span class="n">customTab2</span><span class="p">,</span> <span class="n">customTab3</span><span class="p">;</span>
<span class="n">MyTextView</span> <span class="n">myTextview1</span><span class="p">,</span> <span class="n">myTextview2</span><span class="p">,</span> <span class="n">myTextview3</span><span class="p">;</span>
<span class="nb">string</span> <span class="n">myTextView1Content</span> <span class="p">=</span> <span class="s">"Now is the witness of our discontinent."</span><span class="p">;</span>
<span class="nb">string</span> <span class="n">myTextView2Content</span> <span class="p">=</span> <span class="s">"Four stores and seven pounds ago..."</span><span class="p">;</span>
<span class="nb">string</span> <span class="n">myTextView3Content</span> <span class="p">=</span> <span class="s">"Help me open yon cantelope."</span><span class="p">;</span>
</code></pre></div></div>

<p>We’ve seen all this before. After the tab position variable, we have a prefix for label text (note: this isn’t <code class="language-plaintext highlighter-rouge">Label</code> text, just label text), content strings, and declarations for widgets we’ll be stuffing into the tabs.</p>

<p>The <code class="language-plaintext highlighter-rouge">Notebook</code> constructor–in fact, the rest of the <code class="language-plaintext highlighter-rouge">Notebook</code> class code—is almost the same as we’ve already worked with. The only thing that’s really different is the instantiation of the tab. Here’s what one of those looks like:</p>

<div class="language-d highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">myTextview1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">MyTextView</span><span class="p">(</span><span class="n">myTextView1Content</span><span class="p">);</span>
<span class="n">customTab1</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CustomTab</span><span class="p">(</span><span class="n">tabLabel1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">this</span><span class="p">);</span>
<span class="n">appendPage</span><span class="p">(</span><span class="n">myTextview1</span><span class="p">,</span> <span class="n">customTab1</span><span class="p">);</span>
</code></pre></div></div>

<p>Instead of a <code class="language-plaintext highlighter-rouge">Label</code>, we’re instantiating a <code class="language-plaintext highlighter-rouge">CustomTab</code> object and we’re passing it three arguments:</p>

<ul>
  <li>a string that will appear as the prefix in the customized tab label,</li>
  <li>a number to go with that prefix (so the tab’s name will be <code class="language-plaintext highlighter-rouge">CustomTab 0</code> in this case), and</li>
  <li>a pointer to the <code class="language-plaintext highlighter-rouge">Notebook</code> itself to facilitate communication from the customized tab back to the <code class="language-plaintext highlighter-rouge">Notebook</code>. (This is part of the workaround I mentioned earlier.)</li>
</ul>

<p>Moving right along…</p>

<h2 id="a-signal--callback-for-switching-pages">A Signal &amp; Callback for Switching Pages</h2>

<p>One more thing that’s different in the <code class="language-plaintext highlighter-rouge">Notebook</code> constructor is this line:</p>

<div class="language-d highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">addOnSwitchPage</span><span class="p">(&amp;</span><span class="n">onSwitchPage</span><span class="p">);</span>
</code></pre></div></div>

<p>Here, we’re harnessing a signal that will trigger a callback every time the user clicks on a tab other than the one that’s current. And here’s the callback:</p>

<div class="language-d highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">onSwitchPage</span><span class="p">(</span><span class="n">Widget</span> <span class="n">widget</span><span class="p">,</span> <span class="kt">uint</span> <span class="n">pageNumber</span><span class="p">,</span> <span class="n">Notebook</span> <span class="n">notebook</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">_currentPage</span> <span class="p">=</span> <span class="n">pageNumber</span><span class="p">;</span>
		
	<span class="n">writeln</span><span class="p">(</span><span class="s">"Current page: "</span><span class="p">,</span> <span class="n">_currentPage</span><span class="p">);</span>
			
<span class="p">}</span> <span class="c1">// addOnChangeCurrentPage()</span>
</code></pre></div></div>

<p>It’s straightforward. All it does is report which page is now current. But this callback will play an important part once everything’s hooked up and running because it’s the only way we have of knowing, programmatically, that the page has been switched. More about this later, too.</p>

<h2 id="the-custom-tab-class">The Custom Tab Class</h2>

<div class="language-d highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="n">CustomTab</span> <span class="p">:</span> <span class="n">Box</span>
<span class="p">{</span>
	<span class="n">TabDrawingArea</span> <span class="n">tabDrawingArea</span><span class="p">;</span>
	<span class="n">Notebook</span> <span class="n">_notebook</span><span class="p">;</span>
	
	<span class="k">this</span><span class="p">(</span><span class="nb">string</span> <span class="n">tabLabel</span><span class="p">,</span> <span class="n">Notebook</span> <span class="n">notebook</span><span class="p">,</span> <span class="kt">int</span> <span class="n">tabIndex</span><span class="p">)</span>
	<span class="p">{</span>
		<span class="k">super</span><span class="p">(</span><span class="n">Orientation</span><span class="p">.</span><span class="n">HORIZONTAL</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
		<span class="n">tabDrawingArea</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TabDrawingArea</span><span class="p">(</span><span class="n">tabLabel</span><span class="p">,</span> <span class="n">notebook</span><span class="p">,</span> <span class="n">tabIndex</span><span class="p">);</span>
		<span class="n">packStart</span><span class="p">(</span><span class="n">tabDrawingArea</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
		<span class="n">_notebook</span> <span class="p">=</span> <span class="n">notebook</span><span class="p">;</span>
		<span class="n">showAll</span><span class="p">();</span>
		
	<span class="p">}</span> <span class="c1">// this()</span>
	
<span class="p">}</span> <span class="c1">// class CustomTab</span>
</code></pre></div></div>

<p>This is just a <code class="language-plaintext highlighter-rouge">Box</code>. At the moment, we don’t really need this intermediary widget, but down the road, if we want to stuff more than one child widget into a tab, we will, so here it is.</p>

<p>All this does is serve to contain a <code class="language-plaintext highlighter-rouge">DrawingArea</code>… which it does quite well.</p>

<p>One thing I’ll point out here… this <code class="language-plaintext highlighter-rouge">Box</code> needs to make a call to <code class="language-plaintext highlighter-rouge">showAll()</code> so we get to see what’s in the <code class="language-plaintext highlighter-rouge">Box</code>. Without it, we’d be looking at a blank <code class="language-plaintext highlighter-rouge">DrawingArea</code>.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Over the next two blog posts, we’ll dig further into the customization code. Until next time, take care and may your inspiration always be inspired-ing-ly amazing.</p>

<div class="blog-nav">
	<div style="float: left;">
		<a href="./0078-notebook-ii-multiple-tabs.html">Previous: Notebook II - Multiple Tabs</a>
	</div>
	<div style="float: right;">
		<a href="./0080-notebook-iv-custom-tabs-ii.html">Next: Notebook IV - Custom Tabs II</a>
	</div>
</div>
<p>
	<h3></h3>
<div class="inpage-frame">
	<a href="https://github.com/sponsors/rontarrant">
		<BR>
		<BR>
		<B>Is this article useful? Educational? Entertaining, perhaps?</B>
		<BR>
		<figure class="left">
			<img src="../images/favorite_32.png" alt="Sponsorship heart" style="width: 32px; height: 32px;">
		</figure>
		You're free to accept or decline this invitation to become our newest sponsor.
	</a>
</div>
	<h4 class="comment-blurb"><B>Comments? Questions? Observations?</B></h4>
	<p>
		Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.
	</p>
	<script src="https://utteranc.es/client.js"
		repo="rontarrant"
		issue-term="pathname"
		theme="github-light"
		crossorigin="anonymous"
		async>
	</script>
	<ul>
		<li> come on over to the <a href="https://forum.dlang.org/">D Language Forum</a> and look for one of the <i>gtkDcoding</i> announcement posts, </li>
		<li> drop by <a href="https://forum.gtkd.org/">the <i>GtkD Forum</i></a>,</li>
		<li> follow the link below to email me, or</li>
		<li> go to the	<a href="https://www.facebook.com">gtkDcoding Facebook page.</a></li>
	</ul>
</p>
<p> You can also subscribe <a href="/feed.xml">via RSS</a> so you won't miss anything. Thank you very much for dropping by.</p>
<p>&copy; Copyright 2025 Ron Tarrant </p>
</div>

	<a class="u-url" href="./0079-notebook-iii-custom-tabs-i.html" hidden></a>
</article>

      </div>
    </main><footer class="site-footer h-card">
  <data class="u-url" href="/"></data>

  <div class="wrapper">

    <h2 class="footer-heading">gtkDcoding</h2>

    <div class="footer-col-wrapper">
      <div class="footer-col footer-col-1">
        <ul class="contact-list">
          <li class="p-name">Ron Tarrant</li><li><a class="u-email" href="mailto:gtkDcoding@gmail.com">gtkDcoding@gmail.com</a></li></ul>
      </div>

      <div class="footer-col footer-col-2"><ul class="social-media-list"><li><a href="https://github.com/rontarrant"><svg class="svg-icon"><use xlink:href="../assets/minima-social-icons.svg#github"></use></svg> <span class="username">rontarrant</span></a></li></ul>
</div>

      <div class="footer-col footer-col-3">
        <p>Simple examples of how to use GtkD to build GUI applications.</p>
      </div>
    </div>

  </div>

</footer>
</body>

</html>
